package io.persimmon.core.entity.base;


import io.persimmon.core.exception.PersimmonExceptionFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public interface BaseEnum {
    Map<Class<Enum<?>>, Map<Integer, Enum<?>>> ENUM_CODE_MAP = new ConcurrentHashMap<>();
    Map<Class<Enum<?>>, Map<String, Enum<?>>> ENUM_NAME_MAP = new ConcurrentHashMap<>();

    static <T extends Enum<?>> void registerCode(Class<T> c) {
        synchronized (c) {
            Class<Enum<?>> clazz = (Class<Enum<?>>) c;
            Enum<?>[] enumConstants = clazz.getEnumConstants();

            Map<Integer, Enum<?>> map = new HashMap<>();
            for (Enum<?> e : enumConstants) {
                if (!(e instanceof BaseEnum)) {
                    throw PersimmonExceptionFactory.create(clazz.getName() + " need implements BaseEnum");
                }
                BaseEnum baseEnum = (BaseEnum) e;
                map.put(baseEnum.getCode(), e);
            }
            ENUM_CODE_MAP.put(clazz, map);
        }
    }

    static <T extends Enum<?>> void registerName(Class<T> c) {
        synchronized (c) {
            Class<Enum<?>> clazz = (Class<Enum<?>>) c;
            Enum<?>[] enumConstants = clazz.getEnumConstants();

            Map<String, Enum<?>> map = new HashMap<>();
            for (Enum<?> e : enumConstants) {
                map.put(e.name(), e);
            }
            ENUM_NAME_MAP.put(clazz, map);
        }
    }

    static <T extends Enum<?>> T codeOfNullable(Class<T> c, Integer code) {
        Class<Enum<?>> clazz = (Class<Enum<?>>) c;
        if (!ENUM_CODE_MAP.containsKey(clazz)) {
            registerCode(clazz);
        }
        return (T) ENUM_CODE_MAP.get(clazz).get(code);
    }

    static <T extends Enum<?>> T codeOf(Class<T> c, Integer code) {
        T t = codeOfNullable(c, code);
        if (Objects.isNull(t)) {
            throw PersimmonExceptionFactory.create(c.getName() + " not exits code:" + code);
        }
        return t;
    }

    static <T extends Enum<?>> T nameOfNullable(Class<T> c, String name) {
        Class<Enum<?>> clazz = (Class<Enum<?>>) c;
        if (!ENUM_NAME_MAP.containsKey(clazz)) {
            registerName(c);
        }
        return (T) ENUM_NAME_MAP.get(clazz).get(name);
    }

    static <T extends Enum<?>> T nameOf(Class<T> c, String name) {
        T t = nameOfNullable(c, name);
        if (Objects.isNull(t)) {
            throw PersimmonExceptionFactory.create(c.getName() + " not exits name:" + name);
        }
        return t;
    }


    static <T extends Enum<?>> List<T> values(Class<T> c) {
        Class<Enum<?>> clazz = (Class<Enum<?>>) c;
        Enum<?>[] enumConstants = clazz.getEnumConstants();
        return (List<T>) Arrays.asList(enumConstants)
                .stream()
                .collect(Collectors.toList());
    }

    default Integer getCode() {
        return 0;
    }

    String getDesc();
}
